package checkers;

import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.animation.Animation;
import javafx.animation.FadeTransition;
import javafx.animation.FadeTransitionBuilder;
import javafx.animation.TranslateTransition;
import javafx.animation.TranslateTransitionBuilder;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.binding.NumberBinding;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Duration;
import javax.swing.Timer;

public class CheckersJavaFXGUI extends CheckersGUI {

    private Stage stage;
    private static final int boardGridLenght = 8;
    private static final double squareMarginAmount = 1;
    private NumberBinding minRootSize;
    private NumberBinding boardSquareSize;
    private ImageView goFullScreenIcon;
    private ImageView cpuProcessing;
    private StackPane optionsIcon;
    private Pane piecesLayer;
    private Pane boardLayer;
    private Pane topPane;
    private StackPane root;
    private StackPane[][] pieces;
    private StackPane[][] boardSquares;
    private CheckersState currentState;
    private List<CheckersMovement> tempMovements;
    private double initX;
    private double initY;
    private Point2D dragAnchor;
    private String backgroundColor = "rgb(220,220,220)";
    private String blackSquaresColor = "rgb(90,90,90)";
    private double kingLayerOpacity = 0.40;
    SimpleObjectProperty<Color> blackPieceColorProperty = new SimpleObjectProperty<Color>(Color.gray(0.30));
    SimpleObjectProperty<Color> whitePieceColorProperty = new SimpleObjectProperty<Color>(Color.gray(0.95));
    private List<CheckersMovement> originalAvailableMovements;
    private List<CheckersMovement> currentAvailableMovements;
    private CheckersOperation currentSourceOperation;
    private List<Animation> animationQueue;
    private List<Node> currentAnimationNodes;
    private boolean animationRunning;
    private Duration defaultPiecesAnimationDuration = Duration.seconds(0.5);
    private Duration defaultLayersAnimationDuration = Duration.seconds(0.5);

    private void initGUI() {

        Font.loadFont(this.getClass().getResourceAsStream("/images/Nunito-Regular.ttf"), 0);

        animationQueue = new LinkedList<Animation>();
        currentAnimationNodes = new LinkedList<Node>();
        tempMovements = new LinkedList<CheckersMovement>();

        root = new StackPane();
        root.setStyle("-fx-background-color: " + backgroundColor);

        minRootSize = Bindings.min(root.heightProperty(), root.widthProperty());
        boardSquareSize = Bindings.divide(minRootSize, boardGridLenght + squareMarginAmount * 2);

        initBoard();

        piecesLayer = new Pane();
        piecesLayer.maxHeightProperty().bind(minRootSize);
        piecesLayer.maxWidthProperty().bind(minRootSize);
        root.getChildren().add(piecesLayer);

        ImageView bigLight = new ImageView(new Image(getClass().getResourceAsStream("/images/master_light.png")));
        bigLight.fitHeightProperty().bind(root.heightProperty());
        bigLight.fitWidthProperty().bind(root.widthProperty());
        bigLight.setOpacity(0.4);
        bigLight.setDisable(true);
        root.getChildren().add(bigLight);

        topPane = new StackPane();
        topPane.setDisable(true);
        root.getChildren().add(topPane);

        initIcons();

        stage = new Stage();
        stage.getIcons().add(new Image(getClass().getResourceAsStream("/images/icon_32.png")));
        stage.setTitle("Damas");
        stage.setMinHeight(250);
        stage.setMinWidth(250);
        stage.setScene(new Scene(root, 700, 500));
        stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
            @Override
            public void handle(WindowEvent t) {
                System.exit(0);
            }
        });
        stage.fullScreenProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
                if (t1) {
                    FadeTransitionBuilder.create().node(stage.getScene().getRoot()).fromValue(0).toValue(1).duration(Duration.seconds(1)).build().play();
                    goFullScreenIcon.setImage(new Image(getClass().getResourceAsStream("/images/small.png")));
                } else {
                    goFullScreenIcon.setImage(new Image(getClass().getResourceAsStream("/images/full.png")));
                }
            }
        });
        stage.show();

    }

    public CheckersJavaFXGUI() {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                initGUI();
            }
        });
    }

    private void showCurrentStateBoard() {

        if (!piecesLayer.getChildren().isEmpty()) {
            FadeTransition fadeTransition = FadeTransitionBuilder.create().node(piecesLayer).duration(defaultLayersAnimationDuration).fromValue(1).toValue(0).build();
            fadeTransition.setOnFinished(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent t) {
                    piecesLayer.getChildren().clear();
                    animationRunning = false;
                    playAnimationQueue();
                    showCurrentStateBoard();
                }
            });
            animationQueue.add(fadeTransition);
            playAnimationQueue();
            return;
        }

        //piecesLayer.getChildren().clear();
        piecesLayer.setOpacity(0);
        pieces = new StackPane[boardGridLenght][boardGridLenght];

        Image pieceShadow = new Image(getClass().getResourceAsStream("/images/piece_shadow.png"));

        byte[][] board = currentState.getBoard();
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board.length; j++) {
                if (board[i][j] != CheckersState.BLANK_POSITION) {

                    double circleRadiusProportion = 0.46;

                    ImageView pieceShadowLayer = new ImageView(pieceShadow);
                    pieceShadowLayer.setSmooth(true);
                    pieceShadowLayer.setPreserveRatio(true);
                    pieceShadowLayer.fitHeightProperty().bind(Bindings.multiply(boardSquareSize, circleRadiusProportion * 2.03));

                    final StackPane piece = new StackPane();
                    pieces[i][j] = piece;

                    piece.prefHeightProperty().bind(boardSquareSize);
                    piece.prefWidthProperty().bind(boardSquareSize);

                    Circle circle = new Circle();
                    if (board[i][j] == CheckersState.BLACK_COMMON || board[i][j] == CheckersState.BLACK_KING) {
                        circle.fillProperty().bind(blackPieceColorProperty);
                    } else {
                        circle.fillProperty().bind(whitePieceColorProperty);
                    }

                    circle.radiusProperty().bind(Bindings.multiply(boardSquareSize, circleRadiusProportion));
                    piece.getChildren().add(circle);

                    if (board[i][j] == CheckersState.WHITE_KING || board[i][j] == CheckersState.BLACK_KING) {
                        initKingPiece(piece).setOpacity(kingLayerOpacity);
                    }

                    piece.getChildren().add(pieceShadowLayer);
                    piecesLayer.getChildren().add(piece);

                    final DoubleBinding x = Bindings.multiply(boardSquareSize, j + squareMarginAmount);
                    final DoubleBinding y = Bindings.multiply(boardSquareSize, i + squareMarginAmount);

                    piece.translateYProperty().bind(y);
                    piece.translateXProperty().bind(x);


                    piece.setOnMouseReleased(new EventHandler<MouseEvent>() {
                        public void handle(MouseEvent me) {
                            if (currentAvailableMovements != null && currentAvailableMovements.size() > 0
                                    && currentSourceOperation != null && !currentAnimationNodes.contains(piece)) {
                                //me.consume();

                                piece.setCursor(Cursor.HAND);

                                double dragX = me.getSceneX() - dragAnchor.getX();
                                double dragY = me.getSceneY() - dragAnchor.getY();

                                double newXPosition = initX + dragX;
                                double newYPosition = initY + dragY;

                                int desiredIPosition = (int) ((newXPosition + boardSquareSize.doubleValue() / 2) / boardSquareSize.doubleValue());
                                int desiredJPosition = (int) ((newYPosition + boardSquareSize.doubleValue() / 2) / boardSquareSize.doubleValue());
                                desiredIPosition -= squareMarginAmount;
                                desiredJPosition -= squareMarginAmount;

                                if (desiredIPosition >= 0 && desiredIPosition < boardGridLenght
                                        && desiredJPosition >= 0 && desiredJPosition < boardGridLenght) {

                                    CheckersOperation desiredOperation = new CheckersOperation(desiredJPosition, desiredIPosition, CheckersOperation.TARGET_POSITION);

                                    List<CheckersMovement> newAvailableMovements = new ArrayList<CheckersMovement>();
                                    for (int i = 0; i < currentAvailableMovements.size(); i++) {
                                        CheckersMovement move = currentAvailableMovements.get(i);
                                        CheckersOperation source = move.getFirst(CheckersOperation.SOURCE_POSITION);
                                        CheckersOperation target = move.getFirst(CheckersOperation.TARGET_POSITION);
                                        if (source != null && target != null
                                                && source.equals(currentSourceOperation)
                                                && target.equals(desiredOperation)) {
                                            newAvailableMovements.add(move);
                                            source.operation = CheckersOperation.NULL_OPERATION;
                                            target.operation = CheckersOperation.SOURCE_POSITION;
                                        }
                                    }

                                    if (!newAvailableMovements.isEmpty()) {
                                        createPieceMovementAnimation(piece, desiredIPosition, desiredJPosition);
                                        playAnimationQueue();

                                        CheckersMovement anyMovement = newAvailableMovements.get(0).clone();
                                        int sourceIndex = anyMovement.getFirstIndex(CheckersOperation.SOURCE_POSITION);
                                        int targetIndex = anyMovement.getFirstIndex(CheckersOperation.TARGET_POSITION);
                                        if (targetIndex == -1) {
                                            targetIndex = anyMovement.getMovementsList().size();
                                        }
                                        for (int i = 0; i < sourceIndex + 1; i++) {
                                            anyMovement.getMovementsList().get(i).operation = CheckersOperation.NULL_OPERATION;
                                        }
                                        if (targetIndex != -1) {
                                            for (int i = targetIndex; i < anyMovement.getMovementsList().size(); i++) {
                                                anyMovement.getMovementsList().get(i).operation = CheckersOperation.NULL_OPERATION;
                                            }
                                        }
                                        showMovement(anyMovement);

                                        currentAvailableMovements = newAvailableMovements;
                                        if (newAvailableMovements.size() == 1 && newAvailableMovements.get(0).getLast(CheckersOperation.TARGET_POSITION) == null) {
                                            lastUserChosenMovement = originalAvailableMovements.get(newAvailableMovements.get(0).getId());
                                            /*CheckersMovement clone = lastUserChosenMovement.clone();
                                             for (CheckersOperation op : clone.getMovementsList()) {
                                             if (op.operation == CheckersOperation.SOURCE_POSITION || op.operation == CheckersOperation.TARGET_POSITION) {
                                             op.operation = CheckersOperation.NULL_OPERATION;
                                             }
                                             }
                                             showMovement(clone);*/
                                            originalAvailableMovements = null;
                                            currentAvailableMovements = null;
                                            notificationFromGUI(CheckersGUI.NOTIFY_USER_HAS_CHOOSED_MOVEMENT);
                                        }
                                    } else {
                                        createPieceMovementAnimation(piece, currentSourceOperation.y, currentSourceOperation.x);
                                        playAnimationQueue();
                                    }

                                } else {
                                    createPieceMovementAnimation(piece, currentSourceOperation.y, currentSourceOperation.x);
                                    playAnimationQueue();
                                }
                            }
                        }
                    ;
                    });

                    piece.setOnMouseDragged(new EventHandler<MouseEvent>() {
                        public void handle(MouseEvent me) {
                            if (currentSourceOperation != null && !currentAnimationNodes.contains(piece)) {
                                double dragX = me.getSceneX() - dragAnchor.getX();
                                double dragY = me.getSceneY() - dragAnchor.getY();

                                double newXPosition = initX + dragX;
                                double newYPosition = initY + dragY;

                                piece.translateXProperty().unbind();
                                piece.translateYProperty().unbind();
                                piece.setTranslateX(newXPosition);
                                piece.setTranslateY(newYPosition);
                            }
                        }
                    });

                    piece.setOnMousePressed(new EventHandler<MouseEvent>() {
                        public void handle(MouseEvent me) {
                            currentSourceOperation = null;
                            if (piece.getCursor() == Cursor.HAND) {
                                currentSourceOperation = null;
                                for (int i = 0; i < boardGridLenght; i++) {
                                    for (int j = 0; j < boardGridLenght; j++) {
                                        if (piece == pieces[i][j]) {
                                            currentSourceOperation = new CheckersOperation(i, j, CheckersOperation.SOURCE_POSITION);
                                            break;
                                        }
                                    }
                                }
                                piece.setCursor(Cursor.NONE);
                                piece.toFront();
                                //when mouse is pressed, store initial position
                                initX = piece.getTranslateX();
                                initY = piece.getTranslateY();
                                dragAnchor = new Point2D(me.getSceneX(), me.getSceneY());
                                piece.translateXProperty().unbind();
                                piece.translateYProperty().unbind();
                            }
                        }
                    });

                    piece.setOnMouseEntered(new EventHandler<MouseEvent>() {
                        @Override
                        public void handle(MouseEvent t) {
                            if (currentAvailableMovements != null && currentAvailableMovements.size() > 0) {
                                CheckersOperation wantedSourcePosition = null;
                                for (int i = 0; i < boardGridLenght; i++) {
                                    for (int j = 0; j < boardGridLenght; j++) {
                                        if (piece == pieces[i][j]) {
                                            wantedSourcePosition = new CheckersOperation(i, j, CheckersOperation.SOURCE_POSITION);
                                            break;
                                        }
                                    }
                                }
                                if (wantedSourcePosition != null) {
                                    for (CheckersMovement move : currentAvailableMovements) {
                                        CheckersOperation source = move.getFirst(CheckersOperation.SOURCE_POSITION);
                                        if (source != null && source.equals(wantedSourcePosition)) {
                                            piece.setCursor(Cursor.HAND);
                                            return;
                                        }
                                    }
                                }
                            }
                        }
                    });

                    piece.setOnMouseExited(new EventHandler<MouseEvent>() {
                        @Override
                        public void handle(MouseEvent t) {
                            piece.setCursor(Cursor.DEFAULT);
                        }
                    });
                }
            }
        }

        FadeTransition fadeTransition = FadeTransitionBuilder.create().node(piecesLayer).fromValue(0).toValue(1).duration(defaultLayersAnimationDuration).build();
        fadeTransition.setOnFinished(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                animationRunning = false;
                playAnimationQueue();
            }
        });
        animationQueue.add(fadeTransition);
        playAnimationQueue();

    }

    @Override
    public void showBoard(CheckersState checkersState) {
        currentState = checkersState;
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                showCurrentStateBoard();
            }
        });
    }

    @Override
    public void requestUserMovement(List<CheckersMovement> availableMovements) {
        originalAvailableMovements = availableMovements;
        currentAvailableMovements = new ArrayList<CheckersMovement>();
        short count = 0;
        for (CheckersMovement move : originalAvailableMovements) {
            move.setId(count++);
            currentAvailableMovements.add(move.clone());
        }
    }

    private Text getATextNode(String text) {
        Text newText = new Text(text);
        Font font = Font.font("Nunito", 25);
        newText.setFont(font);
        newText.scaleXProperty().bind(Bindings.multiply(boardSquareSize, 0.025));
        newText.scaleYProperty().bind(Bindings.multiply(boardSquareSize, 0.025));
        newText.fillProperty().bind(blackPieceColorProperty);
        newText.setCursor(Cursor.HAND);
        DropShadow dropShadow = new DropShadow();
        dropShadow.setColor(Color.WHITE);
        dropShadow.setRadius(1);
        dropShadow.setOffsetY(1);
        dropShadow.setSpread(1);
        newText.setEffect(dropShadow);
        return newText;
    }

    private void initIcons() {

        StackPane goFullScreen = new StackPane();
        goFullScreen.maxHeightProperty().bind(boardSquareSize);
        goFullScreen.maxWidthProperty().bind(boardSquareSize);
        goFullScreen.setCursor(Cursor.HAND);
        goFullScreen.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent t) {
                stage.setFullScreen(!stage.isFullScreen());
            }
        });
        StackPane.setAlignment(goFullScreen, Pos.BOTTOM_RIGHT);

        goFullScreenIcon = new ImageView(new Image(getClass().getResourceAsStream("/images/full.png")));
        goFullScreenIcon.setPreserveRatio(true);
        goFullScreenIcon.setSmooth(true);
        goFullScreenIcon.setOpacity(0.3);
        goFullScreenIcon.fitHeightProperty().bind(Bindings.multiply(boardSquareSize, 0.70));
        goFullScreen.getChildren().add(goFullScreenIcon);

        root.getChildren().add(goFullScreen);


        StackPane cpuProcessingPane = new StackPane();
        cpuProcessingPane.maxHeightProperty().bind(boardSquareSize);
        cpuProcessingPane.maxWidthProperty().bind(boardSquareSize);
        StackPane.setAlignment(cpuProcessingPane, Pos.BOTTOM_LEFT);

        cpuProcessing = new ImageView(new Image(getClass().getResourceAsStream("/images/cpu_processing.png")));
        cpuProcessing.setPreserveRatio(true);
        cpuProcessing.setSmooth(true);
        cpuProcessing.setOpacity(0.0);
        cpuProcessing.fitHeightProperty().bind(Bindings.multiply(boardSquareSize, 0.70));
        cpuProcessingPane.getChildren().add(cpuProcessing);

        root.getChildren().add(cpuProcessingPane);


        optionsIcon = new StackPane();
        optionsIcon.maxHeightProperty().bind(boardSquareSize);
        optionsIcon.maxWidthProperty().bind(boardSquareSize);
        StackPane.setAlignment(optionsIcon, Pos.TOP_RIGHT);
        optionsIcon.setCursor(Cursor.HAND);

        ImageView optionsIconImage = new ImageView(new Image(getClass().getResourceAsStream("/images/options.png")));
        optionsIconImage.setPreserveRatio(true);
        optionsIconImage.setSmooth(true);
        optionsIconImage.setOpacity(0.3);
        optionsIconImage.fitHeightProperty().bind(Bindings.multiply(boardSquareSize, 0.70));
        optionsIcon.getChildren().add(optionsIconImage);

        root.getChildren().add(optionsIcon);

        optionsIcon.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent t) {
                optionsIcon.setDisable(true);
                topPane.setDisable(false);
                topPane.setOpacity(0);
                StackPane availableOptions = new StackPane();
                topPane.getChildren().add(availableOptions);

                final Rectangle rectangle = new Rectangle();
                rectangle.heightProperty().bind(topPane.heightProperty());
                rectangle.widthProperty().bind(topPane.widthProperty());
                rectangle.fillProperty().bind(whitePieceColorProperty);
                rectangle.setOpacity(0.8);
                //rectangle.setFill((playerWhoWon == CheckersState.WHITE_PLAYER) ? whitePieceColor : blackPieceColor);
                availableOptions.getChildren().add(rectangle);

                GridPane gridPane = new GridPane();//VBox();
                gridPane.setAlignment(Pos.CENTER);
                gridPane.vgapProperty().bind(Bindings.multiply(boardSquareSize, 0.25));
                availableOptions.getChildren().add(gridPane);

                HBox colorsBox = new HBox();
                colorsBox.setAlignment(Pos.CENTER);
                colorsBox.spacingProperty().bind(Bindings.multiply(boardSquareSize, 0.25));
                gridPane.add(colorsBox, 0, 0);

                List<Color> availableColors = new ArrayList<Color>();
                availableColors.add(Color.gray(0.95));
                availableColors.add(Color.SILVER);
                availableColors.add(Color.DEEPPINK.interpolate(Color.WHITE, 0.15));
                availableColors.add(Color.FIREBRICK.brighter());
                availableColors.add(Color.DARKORANGE);
                availableColors.add(Color.GOLD);
                availableColors.add(Color.LIMEGREEN);
                availableColors.add(Color.SEAGREEN.interpolate(Color.GREEN, 0.2));
                availableColors.add(Color.LIGHTSEAGREEN);
                availableColors.add(Color.DODGERBLUE.desaturate().brighter());
                availableColors.add(Color.STEELBLUE);
                availableColors.add(Color.MEDIUMPURPLE);

                DoubleBinding rectangleSize = Bindings.multiply(boardSquareSize, 0.5);

                for (final Color color : availableColors) {
                    Rectangle rec = new Rectangle();
                    rec.setFill(color);
                    rec.setCursor(Cursor.HAND);
                    rec.heightProperty().bind(rectangleSize);
                    rec.widthProperty().bind(rectangleSize);
                    //rec.setArcHeight(10);
                    //rec.setArcWidth(10);
                    rec.setStroke(Color.WHITE);
                    rec.setOnMouseClicked(new EventHandler<MouseEvent>() {
                        @Override
                        public void handle(MouseEvent t) {
                            Timer timer = new Timer(20, new ActionListener() {
                                private double i = 0;
                                private Color source = whitePieceColorProperty.getValue();
                                private Color target = color;

                                @Override
                                public void actionPerformed(java.awt.event.ActionEvent e) {
                                    if (i < 1) {
                                        whitePieceColorProperty.setValue(source.interpolate(target, i));
                                        i += 0.05;
                                    } else {
                                        try {
                                            this.finalize();
                                        } catch (Throwable ex) {
                                            Logger.getLogger(CheckersJavaFXGUI.class.getName()).log(Level.SEVERE, null, ex);
                                        }
                                    }
                                }
                            });
                            timer.start();
                            /*rectangle.fillProperty().unbind();
                             FillTransition fillTransition = FillTransitionBuilder.create().shape(rectangle).fromValue((Color) (rectangle.getFill())).toValue(color).duration(Duration.seconds(0.1)).build();
                             fillTransition.setOnFinished(new EventHandler<ActionEvent>() {
                             @Override
                             public void handle(ActionEvent t) {
                             whitePieceColorProperty.setValue(color);
                             }
                             });
                             fillTransition.play();*/
                        }
                    });
                    colorsBox.getChildren().add(rec);
                }
                
                Text smartnessLevel = getATextNode("nível da inteligência artificial");
                smartnessLevel.setCursor(Cursor.DEFAULT);
                gridPane.add(smartnessLevel, 0, 2);
                GridPane.setHalignment(smartnessLevel, HPos.CENTER);

                HBox smartnessLevelSelection = new HBox();
                smartnessLevelSelection.setAlignment(Pos.CENTER);
                smartnessLevelSelection.spacingProperty().bind(Bindings.multiply(boardSquareSize, 0.5));

                Text minus = getATextNode("<");
                final Text smartnessValue = getATextNode(String.format("%02d", topAISmartnessLevel));
                Text plus = getATextNode(">");

                smartnessValue.setCursor(Cursor.DEFAULT);
                minus.setOnMouseClicked(new EventHandler<MouseEvent>() {
                    @Override
                    public void handle(MouseEvent t) {
                        if (topAISmartnessLevel > 1) {
                            topAISmartnessLevel--;
                            smartnessValue.setText(String.format("%02d", topAISmartnessLevel));
                            notificationFromGUI(NOTIFY_SMARTNESS_LEVEL_CHANGED);
                        }
                    }
                });
                plus.setOnMouseClicked(new EventHandler<MouseEvent>() {
                    @Override
                    public void handle(MouseEvent t) {
                        topAISmartnessLevel++;
                        smartnessValue.setText(String.format("%02d", topAISmartnessLevel));
                        notificationFromGUI(NOTIFY_SMARTNESS_LEVEL_CHANGED);
                    }
                });

                smartnessLevelSelection.getChildren().addAll(minus, smartnessValue, plus);
                gridPane.add(smartnessLevelSelection, 0, 3);

                final Text goBack = getATextNode("voltar");
                gridPane.add(goBack, 0, 5);
                GridPane.setHalignment(goBack, HPos.CENTER);

                goBack.setOnMouseClicked(new EventHandler<MouseEvent>() {
                    @Override
                    public void handle(MouseEvent t) {
                        goBack.setDisable(true);
                        FadeTransition fadeTransition = FadeTransitionBuilder.create().node(topPane).duration(defaultLayersAnimationDuration).fromValue(1).toValue(0).build();
                        fadeTransition.setOnFinished(new EventHandler<ActionEvent>() {
                            @Override
                            public void handle(ActionEvent t) {
                                topPane.getChildren().clear();
                                optionsIcon.setDisable(false);
                                topPane.setDisable(true);
                            }
                        });
                        fadeTransition.play();
                    }
                });

                final Text newMatch = getATextNode("nova partida");
                gridPane.add(newMatch, 0, 6);
                GridPane.setHalignment(newMatch, HPos.CENTER);

                newMatch.setOnMouseClicked(new EventHandler<MouseEvent>() {
                    @Override
                    public void handle(MouseEvent t) {
                        newMatch.setDisable(true);
                        FadeTransition fadeTransition = FadeTransitionBuilder.create().node(topPane).duration(defaultLayersAnimationDuration).fromValue(1).toValue(0).build();
                        fadeTransition.setOnFinished(new EventHandler<ActionEvent>() {
                            @Override
                            public void handle(ActionEvent t) {
                                topPane.getChildren().clear();
                                optionsIcon.setDisable(false);
                                topPane.setDisable(true);
                                notificationFromGUI(NOTIFY_NEW_GAME);
                            }
                        });
                        fadeTransition.play();
                    }
                });

                FadeTransitionBuilder.create().node(topPane).duration(defaultLayersAnimationDuration).fromValue(0).toValue(1).build().play();
            }
        });


    }

    private void initBoard() {

        boardLayer = new Pane();
        boardLayer.maxHeightProperty().bind(minRootSize);
        boardLayer.maxWidthProperty().bind(minRootSize);
        root.getChildren().add(boardLayer);

        Image squareShadow = new Image(getClass().getResourceAsStream("/images/square_shadow.png"));

        boardSquares = new StackPane[boardGridLenght][boardGridLenght];
        for (int i = 0; i < boardGridLenght; i++) {
            for (int j = 0; j < boardGridLenght; j++) {
                if (i % 2 != j % 2) {

                    boardSquares[i][j] = new StackPane();
                    boardSquares[i][j].prefHeightProperty().bind(boardSquareSize);
                    boardSquares[i][j].prefWidthProperty().bind(boardSquareSize);
                    boardSquares[i][j].setStyle("-fx-background-color: " + blackSquaresColor);

                    ImageView squareShadowLayer = new ImageView(squareShadow);
                    squareShadowLayer.setSmooth(true);
                    squareShadowLayer.setPreserveRatio(true);
                    squareShadowLayer.fitHeightProperty().bind(boardSquareSize);
                    boardSquares[i][j].getChildren().add(squareShadowLayer);

                    boardSquares[i][j].translateXProperty().bind(Bindings.multiply(boardSquareSize, j + squareMarginAmount));
                    boardSquares[i][j].translateYProperty().bind(Bindings.multiply(boardSquareSize, i + squareMarginAmount));

                    boardLayer.getChildren().add(boardSquares[i][j]);

                }
            }
        }
    }

    private Node initKingPiece(Pane piece) {
        ImageView kingLayer = new ImageView(new Image(getClass().getResourceAsStream("/images/king_simbol.png")));
        kingLayer.setPreserveRatio(true);
        kingLayer.setSmooth(true);
        kingLayer.setOpacity(0.0);
        kingLayer.fitHeightProperty().bind(Bindings.multiply(boardSquareSize, 0.5));
        piece.getChildren().add(kingLayer);
        return kingLayer;
    }

    private void playAnimationQueue() {
        if (!animationRunning && animationQueue != null && animationQueue.size() > 0) {
            animationRunning = true;
            Animation removed = animationQueue.remove(0);
            removed.play();
        }
    }

    private void createPieceMovementAnimation(final Node piece, final byte toBoardX, final byte toBoardY) {

        CheckersOperation source = null;
        for (int i = 0; i < boardGridLenght; i++) {
            for (int j = 0; j < boardGridLenght; j++) {
                if (piece == pieces[i][j]) {
                    source = new CheckersOperation(i, j, CheckersOperation.SOURCE_POSITION);
                    break;
                }
            }
        }
        if (source != null && (source.y != toBoardX || source.x != toBoardY)) {
            pieces[toBoardY][toBoardX] = pieces[source.x][source.y];
            pieces[source.x][source.y] = null;
        }

        currentAnimationNodes.add(piece);
        piece.translateXProperty().unbind();
        piece.translateYProperty().unbind();
        piece.toFront();

        TranslateTransition translateTransition = TranslateTransitionBuilder.create().node(piece).toX((toBoardX + squareMarginAmount) * boardSquareSize.doubleValue()).toY((toBoardY + squareMarginAmount) * boardSquareSize.doubleValue()).duration(defaultPiecesAnimationDuration).build();
        translateTransition.setOnFinished(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                currentAnimationNodes.remove(piece);
                if (!currentAnimationNodes.contains(piece)) {
                    piece.translateYProperty().bind(Bindings.multiply(boardSquareSize, toBoardY + squareMarginAmount));
                    piece.translateXProperty().bind(Bindings.multiply(boardSquareSize, toBoardX + squareMarginAmount));
                }
                animationRunning = false;
                playAnimationQueue();
            }
        });
        animationQueue.add(translateTransition);

    }

    private void createPieceMovementAnimation(Node piece, int toBoardX, int toBoardY) {
        createPieceMovementAnimation(piece, (byte) toBoardX, (byte) toBoardY);
    }

    @Override
    public void showMovement(CheckersMovement movement) {
        tempMovements.add(movement);
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                showTempMovement();
            }
        });
    }

    public void showTempMovement() {
        CheckersMovement movement = tempMovements.remove(0);
        CheckersOperation source = null;

        for (CheckersOperation operation : movement.getMovementsList()) {

            switch (operation.operation) {
                case CheckersOperation.SOURCE_POSITION:
                    source = operation;
                    break;
                case CheckersOperation.TARGET_POSITION:
                    if (source != null) {
                        createPieceMovementAnimation(pieces[source.x][source.y], operation.y, operation.x);
                        source = operation;
                    }
                    break;
                case CheckersOperation.REMOVE_PIECE:
                    final Pane piece = pieces[operation.x][operation.y];
                    for (int i = 0; i < boardGridLenght; i++) {
                        for (int j = 0; j < boardGridLenght; j++) {
                            if (pieces[i][j] == piece) {
                                pieces[i][j] = null;
                                break;
                            }
                        }
                    }
                    currentAnimationNodes.add(piece);
                    FadeTransition removePieceFadeTransition = FadeTransitionBuilder.create().node(piece).fromValue(1).toValue(0).duration(defaultPiecesAnimationDuration).build();
                    removePieceFadeTransition.setOnFinished(new EventHandler<ActionEvent>() {
                        @Override
                        public void handle(ActionEvent t) {
                            currentAnimationNodes.remove(piece);
                            piecesLayer.getChildren().remove(piece);
                            animationRunning = false;
                            playAnimationQueue();
                        }
                    });
                    animationQueue.add(removePieceFadeTransition);
                    break;
                case CheckersOperation.BECOME_KING:
                    final Pane newKingPiece = pieces[operation.x][operation.y];
                    currentAnimationNodes.add(newKingPiece);
                    Node kingLayer = initKingPiece(pieces[operation.x][operation.y]);
                    final byte x = operation.x;
                    final byte y = operation.y;
                    FadeTransition kingFadeTransition = FadeTransitionBuilder.create().node(kingLayer).fromValue(0).toValue(kingLayerOpacity).duration(defaultPiecesAnimationDuration).build();
                    kingFadeTransition.setOnFinished(new EventHandler<ActionEvent>() {
                        @Override
                        public void handle(ActionEvent t) {
                            currentAnimationNodes.remove(newKingPiece);
                            if (!currentAnimationNodes.contains(newKingPiece)) {
                                newKingPiece.translateYProperty().bind(Bindings.multiply(boardSquareSize, x + squareMarginAmount));
                                newKingPiece.translateXProperty().bind(Bindings.multiply(boardSquareSize, y + squareMarginAmount));
                            }
                            animationRunning = false;
                            playAnimationQueue();
                        }
                    });
                    animationQueue.add(kingFadeTransition);
                    break;
            }
        }
        playAnimationQueue();
    }

    @Override
    public void showAIProcessing(final boolean aiProcessing) {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                if (aiProcessing) {
                    cpuProcessing.setOpacity(0.3);
                } else {
                    cpuProcessing.setOpacity(0.0);
                }
            }
        });
    }

    @Override
    public void showGameEndedMessage(final byte playerWhoWon) {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {

                topPane.setOpacity(0);
                topPane.setDisable(false);

                Rectangle rectangle = new Rectangle();
                rectangle.heightProperty().bind(Bindings.multiply(boardSquareSize, 3));
                rectangle.widthProperty().bind(topPane.widthProperty());
                rectangle.setOpacity(0.8);
                rectangle.fillProperty().bind((playerWhoWon == CheckersState.WHITE_PLAYER) ? whitePieceColorProperty : blackPieceColorProperty);
                topPane.getChildren().add(rectangle);
                final Text text = getATextNode("nova partida");
                text.fillProperty().bind((playerWhoWon == CheckersState.WHITE_PLAYER) ? blackPieceColorProperty : whitePieceColorProperty);


                topPane.getChildren().add(text);

                FadeTransition fadeTransition = FadeTransitionBuilder.create().node(topPane).fromValue(0).toValue(1).duration(defaultLayersAnimationDuration).build();
                fadeTransition.setOnFinished(new EventHandler<ActionEvent>() {
                    @Override
                    public void handle(ActionEvent t) {
                        animationRunning = false;
                        playAnimationQueue();
                    }
                });
                animationQueue.add(fadeTransition);

                text.setOnMouseClicked(new EventHandler<MouseEvent>() {
                    @Override
                    public void handle(MouseEvent t) {
                        topPane.setDisable(true);
                        //text.setDisable(true);
                        FadeTransition fadeTransition2 = FadeTransitionBuilder.create().node(topPane).fromValue(1).toValue(0).duration(defaultLayersAnimationDuration).build();
                        animationQueue.add(fadeTransition2);
                        fadeTransition2.setOnFinished(new EventHandler<ActionEvent>() {
                            @Override
                            public void handle(ActionEvent t) {
                                topPane.getChildren().clear();
                                animationRunning = false;
                                playAnimationQueue();
                                notificationFromGUI(NOTIFY_NEW_GAME);
                            }
                        });
                        playAnimationQueue();
                    }
                });
                playAnimationQueue();
            }
        });
    }
}
